Learning Stanza: Quick Reference¶
JITX is implemented in the LB Stanza (Stanza) programming language. We like stanza because it supports types to help write correct code, and also allows higher-level programming. We can write Stanza programs inline with JITX code, to generate designs, components, and modules programmatically.
We do not need all of Stanza to develop with JITX; this document provides a short walkthrough of commonly-used Stanza features.
Comments¶
; this is a comment
;<A>
This is a block comment
;<B>
block comments can be nested with optional tags.
;<B>
;<A>
Basics¶
defpackage learn-stanza-in-y:
import core
import collections
;=========================================================================
; The basics, things you'd find in most programming languages
;=========================================================================
; Variables can be mutable (var) or immutable (val)
var mutable = "this one can be"
val immutable = "this string can't be changed"
; The basic data types (annotations are optional)
val an-int: Int = 12345
val a-long: Long = 12345L
val a-float: Float = 1.2345f
val a-double: Double = 3.14159
val a-string: String = "this is a string"
val a-multiline-string = \<tag>
this is a multi-line string literal.
<tag>
; print formatted strings with println() and "..." % [...]
println("this is a formatted string %_ %_" % [mutable, immutable])
; Stanza is optionally typed, and has a ? (any) type.
var anything:? = 0
anything = 3.14159
anything = "a string"
; Stanza has basic collections such as Tuples, Arrays, Vectors, HashTables, etc.
val tuple: Tuple<?> = [mutable, immutable]
val array = Array<?>(3)
array[0] = "string"
array[1] = 1
array[2] = 1.23455
; array[3] = "out-of-bounds" ; arrays are bounds-checked
val vector = Vector<?>()
vector[0] = "string"
vector[1] = 1
vector[2] = 3.14159
val hash-table = HashTable<String, ?>()
hash-table["0"] = 0
hash-table["1"] = 1
hash-table["2"] = 1
Functions¶
; Functions are declared with the `defn` keyword
defn my-function (arg:?) : ; note the space between identifier and arg list
println("called my-function with %_" % [arg])
my-function("arg") ; note the lack of a space to call the function
; If you try to define a function without a space, the syntax highlighter
; should give you a gentle nudge to fix it (ie, the red text and underline)
defn invalid-func(arg1, arg2) -> Int:
...
; functions can be declared inside another function and capture their environment. This is called a closure.
defn outer (arg):
defn inner ():
println("outer had arg: %_" % [arg])
inner()
outer("something")
; functions are "first-class" in stanza, meaning you can assign variables
; to functions and pass functions as arguments to other functions.
val a-function = outer
defn do-n-times (arg, func, n:Int):
for i in 0 to n do :
func(arg)
do-n-times("argument", a-function, 3)
; sometimes you want to define a function inline, or use an anonymous function.
; for this you can use the syntax:
; fn (args):
; ...
do-n-times("hello", fn (arg): println(arg), 2)
; there is a shorthand for writing anonymous functions, the above can be written
do-n-times("hello", { println(_) }, 2)
; the short hand works for multiple arguments as well.
val multi-lambda = { println(_ + 2 * _) }
multi-lambda(1, 2)
; You can reference the arguments explicitly as well
; with `_0`, `_1`, for the `Nth` argument.
val lambda-args = { println(_0, + (3 * _1)) }
User Defined Types¶
; Structs are declared with the `defstruct` keyword
defstruct MyStruct:
field:String
; constructors are derived automatically
val my-struct = MyStruct("field:value")
; fields are accessed using function-call syntax
println(field(my-struct))
; Stanza supports subtyping with a "multimethod" system based on method
; overloading.
;
; This effectively defines an interface for type `Mytype` consisting
; of a single method `a-method`
deftype MyType
defmulti a-method (m:MyType)
defstruct Foo <: MyType:
fab:Int
defmethod a-method (a-foo: Foo):
println("called a-method on a Foo")
defstruct Bar <: MyType:
bab:Double
defmethod a-method (a-foo: Bar):
println("called a-method on a Bar")
The Type System¶
; True and False are types with a single value.
val a-true: True = true
val a-false: False = false
; You can declare a union type, or a value that is one of a set of types
val a-boolean: True|False = true
val another-boolean: True|False = false
; You can pattern match on types
match(a-boolean):
(t:True): println("is true")
(f:False): println("is false")
; You can match against a single possible type
match(a-boolean:True):
println("is still true")
else:
println("is not true")
; You can compose program logic around the type of a variable
if anything is Float :
println("anything is a float")
else if anything is-not String :
println("anything is not an int")
else :
println("I don't know what anything is")
Control Flow¶
; stanza has the standard basic control flow
val condition = [false, false]
if condition[0] :
; do something
false
else if condition[1] :
; do another thing
false
else :
; whatever else
false
; there is also a switch statement, which can be used to pattern match
; on values (as opposed to types)
switch(anything):
"this": false
"that": false
"the-other-thing": false
else: false
; for and while loops are supported
while condition[0]:
println("do stuff")
; More on For Loops shortly.
for i in 0 to 10 do:
vector[i] = i
; stanza also supports named labels which can function as break or return
; statements
defn another-fn ():
label<False> return:
label<False> break:
while true:
if condition[0] is False:
break(false)
return(false)
; Stanza has exceptions and provides a syntax similar to Python:
try:
some-func()
throw(Exception("Abort!"))
catch (e:Exception):
println("Error: %~" % [e])
finally:
println("We made it!")
; For a comprehensive guide on Stanza's advanced control flow, check out
; this page: http://lbstanza.org/chapter9.html from Stanza-by-Example
For Loop Operators¶
; The for-loop has a special expression at the end called the "Operating Function"
; In most cases - it is simply the `do` operator:
for i in 0 to 10 do:
println("Index: %_" % [i])
; However the operating function can _do_ so much more!
; Operating functions typically have the form:
defn some-op (func, xs:Seqable) -> False:
...
; Here the first argument is a function, and the second argument
; is a sequence of objects
; Referring to our previous example:
for i in 0 to 10 do:
println("Index: %_" % [i])
; When used in a for-loop, the body of the for-loop becomes the function
; `func`.
defn body (i) -> False :
println("Index: %_" % [i])
; The value `i` becomes the argument to `func`. The expression `0 to 10` becomes `xs`.
; There are many operating functions - `seq`, `map`, `filter`, etc. - defined in the stanza core library.
; https://lbstanza.org/reference.html#anchor478
Sequences¶
; for "loops" are sugar for a more powerful syntax.
val xs = [1, 2, 3]
val ys = ['a', 'b', 'c']
val zs = ["foo", "bar", "baz"]
for (x in xs, y in ys, z in zs) do :
println("x:%_, y:%_, z:%_" % [x, y, z])
;xs, ys, and zs are all "Seqable" meaning they are Seq types (sequences).
; Sequences are called "iterators" in other languages, like Python, and
; provide similar abstractions.
;
; A common sequence task is concatenating sequences. This is accomplished
; using the `seq-cat` operating function. This is analogous to
; "flattening" iterators
val concat = to-tuple $
for sequence in [xs, ys, zs] seq-cat:
sequence
; we can also use a variation to interleave the elements of multiple sequences
val interleaved = to-tuple $
for (x in xs, y in ys, z in zs) seq-cat :
[x, y, z]
println("[%,] [%,]" % [concat, interleaved])
; Another common task is mapping a sequence to another, for example multiplying
; all the elements of a list of numbers by a constant. To do this we use `seq`.
var numbers = [1.0, 2.0, 3.0, 4.0]
numbers = to-tuple $
for n in numbers seq :
2.0 * n
println("%," % [numbers])
if find({_ == 2.0}, numbers) is-not False :
println("found it!")
; or maybe we just want to know if there's something in a sequence
var is-there = for n in numbers any? :
n == 2.0
; since this is "syntactic sugar" we can write it explicitly using an
; anonymous function
is-there = any?({_ == 2.0}, numbers)
Documentation¶
;=========================================================================
; Documentation
;=========================================================================
;
; Top level statements can be prefixed with the "doc" field which takes
; a string value and is used to autogenerate documentation for the package.
doc: \<doc>
# Document Strings
```stanza
val you-can = "include code snippets, too"
```
To render documentation as markdown (compatible with mdbook)
```bash
stanza doc source.stanza -o docs
```
<doc>
defn docfn () : false